热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

奖牌|银牌_Python绘制惊艳的桑基图

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Python绘制惊艳的桑基图相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Python 绘制惊艳的桑基图相关的知识,希望对你有一定的参考价值。



导读:本文中,我们使用 Python 的 plotly 绘制桑基图。


作者:云朵君


来源:数据STUDIO(ID:jim_learning)


01 桑基图简介


很多时候,我们需要一种必须可视化数据如何在实体之间流动的情况。例如,以居民如何从一个国家迁移到另一个国家为例。这里演示了有多少居民从英格兰迁移到北爱尔兰、苏格兰和威尔士。



从这个桑基图 (Sankey)可视化中可以明显看出,从England迁移到Wales的居民多于从Scotland或Northern Ireland迁移的居民。


什么是桑基图?


桑基图通常描绘从一个实体(或节点)到另一个实体(或节点)的数据流。


数据流向的实体被称为节点,数据流起源的节点是源节点(例如左侧的England),流结束的节点是目标节点(例如右侧的Wales)。源节点和目标节点通常表示为带有标签的矩形。


流动本身由直线或曲线路径表示,称为链接流/链接的宽度与流的量/数量成正比。在上面的例子中,从英格兰到威尔士的流动(即居民迁移)比从英格兰到苏格兰或北爱尔兰的流动(即居民迁移)更广泛(更多),表明迁移到威尔士的居民数量多于其他国家。


桑基图可用于表示能量、金钱、成本的流动,以及任何具有流动概念的事物。


米纳尔关于拿破仑入侵俄罗斯的经典图表可能是桑基图表最著名的例子。这种使用桑基图的可视化非常有效地显示了法国军队在前往俄罗斯和返回的途中是如何进步(或减少?)的。



本文中,我们使用 Python 的 plotly 绘制桑基图。


02 如何绘制桑基图?


本文使用 2021 年奥运会数据集绘制桑基图。该数据集包含有关奖牌总数的详细信息——国家、奖牌总数以及金牌、银牌和铜牌的单项总数。我们通过绘制一个桑基图来了解一个国家赢得的金牌、银牌和铜牌数。


df_medals = pd.read_excel("data/Medals.xlsx")
print(df_medals.info())
df_medals.rename(columns='Team/NOC':'Country', 'Total': 'Total Medals', 'Gold':'Gold Medals', 'Silver': 'Silver Medals', 'Bronze': 'Bronze Medals', inplace=True)
df_medals.drop(columns=['Unnamed: 7','Unnamed: 8','Rank by Total'], inplace=True)
df_medals


RangeIndex: 93 entries, 0 to 92
Data columns (total 9 columns):
#   Column         Non-Null Count  Dtype  
---  ------         --------------  -----  
0   Rank           93 non-null     int64  
1   Team/NOC       93 non-null     object
2   Gold           93 non-null     int64  
3   Silver         93 non-null     int64  
4   Bronze         93 non-null     int64  
5   Total          93 non-null     int64  
6   Rank by Total  93 non-null     int64  
7   Unnamed: 7     0 non-null      float64
8   Unnamed: 8     1 non-null      float64
dtypes: float64(2), int64(6), object(1)
memory usage: 6.7+ KB
None


1. 桑基图绘图基础


使用 plotly 的 go.Sankey,该方法带有2 个参数 —— nodes  和 links (节点和链接)。


注意:所有节点——源和目标都应该有唯一的标识符。


在本文奥林匹克奖牌数据集情况中:


Source是国家。将前 3 个国家(美国、中国和日本)视为源节点。用以下(唯一的)标识符、标签和颜色来标记这些源节点:


  • 0:美国:绿色

  • 1:中国:蓝色

  • 2:日本:橙色


Target是金牌、银牌或铜牌。用以下(唯一的)标识符、标签和颜色来标记这些目标节点:


  • 3:金牌:金色

  • 4:银牌:银色

  • 5:铜牌:棕色


Link(源节点和目标节点之间)是每种类型奖牌的数量。在每个源中有3个链接,每个链接都以目标结尾——金牌、银牌和铜牌。所以总共有9个链接。每个环节的宽度应为金牌、银牌和铜牌的数量。用以下源标记这些链接到目标、值和颜色:


  • 0 (美国) 至 3,4,5 : 39, 41, 33

  • 1 (中国) 至 3,4,5 : 38, 32, 18

  • 2 (日本) 至 3,4,5 : 27, 14, 17


需要实例化 2 个 python dict 对象来表示


  • nodes (源和目标):标签和颜色作为单独的列表和

  • links:源节点、目标节点、值(宽度)和链接的颜色作为单独的列表


并将其传递给plotly的 go.Sankey


列表的每个索引(标签、源、目标、值和颜色)分别对应一个节点或链接。


NODES = dict( 
#         0                           1                             2        3       4         5                         
label = ["United States of America", "People's Republic of China", "Japan", "Gold", "Silver", "Bronze"],
color = ["seagreen",                 "dodgerblue",                 "orange", "gold", "silver", "brown" ],)
LINKS = dict(   
  source = [  0,  0,  0,  1,  1,  1,  2,  2,  2], # 链接的起点或源节点
  target = [  3,  4,  5,  3,  4,  5,  3,  4,  5], # 链接的目的地或目标节点
  value =  [ 39, 41, 33, 38, 32, 18, 27, 14, 17], # 链接的宽度(数量)
# 链接的颜色
# 目标节点:       3-Gold          4-Silver        5-Bronze
  color = [   
  "lightgreen",   "lightgreen",   "lightgreen",      # 源节点:0 - 美国 States of America
  "lightskyblue", "lightskyblue", "lightskyblue",    # 源节点:1 - 中华人民共和国China
  "bisque",       "bisque",       "bisque"],)        # 源节点:2 - 日本
data = go.Sankey(node = NODES, link = LINKS)
fig = go.Figure(data)
fig.show()


这是一个非常基本的桑基图。但是否注意到图表太宽并且银牌出现在金牌之前?


接下来介绍如何调整节点的位置和宽度。


2. 调整节点位置和图表宽度


为节点添加 x 和 y 位置以明确指定节点的位置。值应介于 0 和 1 之间。


NODES = dict( 
#         0                           1                             2        3       4         5                         
label = ["United States of America", "People's Republic of China", "Japan", "Gold", "Silver", "Bronze"],
color = ["seagreen",                 "dodgerblue",                 "orange", "gold", "silver", "brown" ],)
x = [     0,                          0,                            0,        0.5,    0.5,      0.5],
y = [     0,                          0.5,                          1,        0.1,    0.5,        1],)
data = go.Sankey(node = NODES, link = LINKS)
fig = go.Figure(data)
fig.update_layout(title="Olympics - 2021: Country &  Medals",  font_size=16)
fig.show()

于是得到了一个紧凑的桑基图:



下面看看代码中传递的各种参数如何映射到图中的节点和链接。



▲代码如何映射到桑基图


3. 添加有意义的悬停标签


我们都知道plotly绘图是交互的,我们可以将鼠标悬停在节点和链接上以获取更多信息。



▲带有默认悬停标签的桑基图


当将鼠标悬停在图上,将会显示详细信息。悬停标签中显示的信息是默认文本:节点、节点名称、传入流数、传出流数和总值。


例如:


  • 节点美国共获得11枚奖牌(=39金+41银+33铜)

  • 节点金牌共有104枚奖牌(=美国39枚,中国38枚,日本27枚)


如果我们觉得这些标签太冗长了,我们可以对此进程改进。使用hovertemplate参数改进悬停标签的格式


  • 对于节点,由于hoverlabels 没有提供新信息,通过传递一个空hovertemplate = ""来去掉hoverlabel

  • 对于链接,可以使标签简洁,格式为-

  • 对于节点和链接,让我们使用后缀"Medals"显示值。例如 113 枚奖牌而不是 113 枚。这可以通过使用具有适当valueformatvaluesuffixupdate_traces函数来实现。


NODES = dict( 
#         0                           1                               2        3       4           5
label = ["United States of America", "People's Republic of China",   "Japan", "Gold", "Silver", "Bronze"],
color = [                "seagreen",                 "dodgerblue",  "orange", "gold", "silver", "brown" ],
x     = [                         0,                            0,         0,    0.5,      0.5,      0.5],
y     = [                         0,                          0.5,         1,    0.1,      0.5,        1],
hovertemplate=" ",)
LINK_LABELS = []
for country in ["USA","China","Japan"]:
    for medal in ["Gold","Silver","Bronze"]:
        LINK_LABELS.append(f"country-medal")
LINKS = dict(source = [  0,  0,  0,  1,  1,  1,  2,  2,  2], 
       # 链接的起点或源节点
       target = [  3,  4,  5,  3,  4,  5,  3,  4,  5], 
       # 链接的目的地或目标节点
       value =  [ 39, 41, 33, 38, 32, 18, 27, 14, 17], 
       # 链接的宽度(数量) 
             # 链接的颜色
             # 目标节点:3-Gold          4 -Silver        5-Bronze
             color = ["lightgreen",   "lightgreen",   "lightgreen",   # 源节点:0 - 美国
                      "lightskyblue", "lightskyblue", "lightskyblue", # 源节点:1 - 中国
                      "bisque",       "bisque",       "bisque"],      # 源节点:2 - 日本
             label = LINK_LABELS, 
             hovertemplate="%label",)
data = go.Sankey(node = NODES, link = LINKS)
fig = go.Figure(data)
fig.update_layout(title="Olympics - 2021: Country &  Medals",  
                  font_size=16, width=1200, height=500,)
fig.update_traces(valueformat='3d', 
                  valuesuffix='Medals', 
                  selector=dict(type='sankey'))
fig.update_layout(hoverlabel=dict(bgcolor="lightgray",
                                  font_size=16,
                                  font_family="Rockwell"))
fig.show("png") #fig.show()


▲带有改进的悬停标签的桑基图


4. 对多个节点和级别进行泛化


相对于链接,节点被称为源和目标。作为一个链接目标的节点可以是另一个链接的源。


  • 该代码可以推广到处理数据集中的所有国家。

  • 还可以将图表扩展到另一个层次,以可视化各国的奖牌总数。


NUM_COUNTRIES = 5
X_POS, Y_POS = 0.5, 1/(NUM_COUNTRIES-1)
NODE_COLORS = ["seagreen", "dodgerblue", "orange", "palevioletred", "darkcyan"]
LINK_COLORS = ["lightgreen", "lightskyblue", "bisque", "pink", "lightcyan"]
source = []
node_x_pos, node_y_pos = [], []
node_labels, node_colors = [], NODE_COLORS[0:NUM_COUNTRIES]
link_labels, link_colors, link_values = [], [], [] 
# 第一组链接和节点
for i in range(NUM_COUNTRIES):
    source.extend([i]*3)
    node_x_pos.append(0.01)
    node_y_pos.append(round(i*Y_POS+0.01,2))
    country = df_medals['Country'][i]
    node_labels.append(country) 
    for medal in ["Gold", "Silver", "Bronze"]:
        link_labels.append(f"country-medal")
        link_values.append(df_medals[f"medal Medals"][i])
    link_colors.extend([LINK_COLORS[i]]*3)
source_last = max(source)+1
target = [ source_last, source_last+1, source_last+2] * NUM_COUNTRIES
target_last = max(target)+1
node_labels.extend(["Gold", "Silver", "Bronze"])
node_colors.extend(["gold", "silver", "brown"])
node_x_pos.extend([X_POS, X_POS, X_POS])
node_y_pos.extend([0.01, 0.5, 1])
# 最后一组链接和节点
source.extend([ source_last, source_last+1, source_last+2])
target.extend([target_last]*3)
node_labels.extend(["Total Medals"])
node_colors.extend(["grey"])
node_x_pos.extend([X_POS+0.25])
node_y_pos.extend([0.5])
for medal in ["Gold","Silver","Bronze"]:
    link_labels.append(f"medal")
    link_values.append(df_medals[f"medal Medals"][:i+1].sum())
link_colors.extend(["gold", "silver", "brown"])
print("node_labels", node_labels)
print("node_x_pos", node_x_pos); print("node_y_pos", node_y_pos)
node_labels ['United States of America', "People's Republic of China",
            'Japan', 'Great Britain', 'ROC', 'Gold', 'Silver',
            'Bronze', 'Total Medals']
node_x_pos [0.01, 0.01, 0.01, 0.01, 0.01, 0.5, 0.5, 0.5, 0.75]
node_y_pos [0.01, 0.26, 0.51, 0.76, 1.01, 0.01, 0.5, 1, 0.5]
# 显示的图
NODES = dict(pad  = 20, thickness = 20, 
             line = dict(color = "lightslategrey",
                         width = 0.5),
             hovertemplate=" ",
             label = node_labels, 
             color = node_colors,
             x = node_x_pos, 
             y = node_y_pos, )
LINKS = dict(source = source, 
             target = target, 
             value = link_values, 
             label = link_labels, 
             color = link_colors,
             hovertemplate="%label",)
data = go.Sankey(arrangement='snap', 
                 node = NODES, 
                 link = LINKS)
fig = go.Figure(data)
fig.update_traces(valueformat='3d', 
                  valuesuffix=' Medals', 
                  selector=dict(type='sankey'))
fig.update_layout(title="Olympics - 2021: Country &  Medals",  
                  font_size=16,  
                  width=1200,
                  height=500,)
fig.update_layout(hoverlabel=dict(bgcolor="grey", 
                                  font_size=14, 
                                  font_family="Rockwell"))
fig.show("png")



延伸阅读👇



延伸阅读《利用Python进行数据分析》


干货直达👇


  • 详解6G系统数据治理方案的设计要点和原则

  • 终于有人把监督学习讲明白了

  • 微服务治理框架的选择:对比Spring Cloud和Istio

  • 为什么Web 3.0就是元宇宙?


更多精彩👇


在公众号对话框输入以下关键词


查看更多优质内容!


读书 | 书单 | 干货 讲明白 | 神操作 | 手把手


大数据 | 云计算 | 数据库 | Python | 爬虫 | 可视化


AI | 人工智能 | 机器学习 | 深度学习 | NLP


5G | 中台 | 用户画像 数学 | 算法 数字孪生


据统计,99%的大咖都关注了这个公众号


👇


推荐阅读
  • 本文讨论了如何使用IF函数从基于有限输入列表的有限输出列表中获取输出,并提出了是否有更快/更有效的执行代码的方法。作者希望了解是否有办法缩短代码,并从自我开发的角度来看是否有更好的方法。提供的代码可以按原样工作,但作者想知道是否有更好的方法来执行这样的任务。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 2018年人工智能大数据的爆发,学Java还是Python?
    本文介绍了2018年人工智能大数据的爆发以及学习Java和Python的相关知识。在人工智能和大数据时代,Java和Python这两门编程语言都很优秀且火爆。选择学习哪门语言要根据个人兴趣爱好来决定。Python是一门拥有简洁语法的高级编程语言,容易上手。其特色之一是强制使用空白符作为语句缩进,使得新手可以快速上手。目前,Python在人工智能领域有着广泛的应用。如果对Java、Python或大数据感兴趣,欢迎加入qq群458345782。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • Oracle分析函数first_value()和last_value()的用法及原理
    本文介绍了Oracle分析函数first_value()和last_value()的用法和原理,以及在查询销售记录日期和部门中的应用。通过示例和解释,详细说明了first_value()和last_value()的功能和不同之处。同时,对于last_value()的结果出现不一样的情况进行了解释,并提供了理解last_value()默认统计范围的方法。该文对于使用Oracle分析函数的开发人员和数据库管理员具有参考价值。 ... [详细]
  • 本文介绍了游标的使用方法,并以一个水果供应商数据库为例进行了说明。首先创建了一个名为fruits的表,包含了水果的id、供应商id、名称和价格等字段。然后使用游标查询了水果的名称和价格,并将结果输出。最后对游标进行了关闭操作。通过本文可以了解到游标在数据库操作中的应用。 ... [详细]
  • 也就是|小窗_卷积的特征提取与参数计算
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了卷积的特征提取与参数计算相关的知识,希望对你有一定的参考价值。Dense和Conv2D根本区别在于,Den ... [详细]
  • 解决python matplotlib画水平直线的问题
    本文介绍了在使用python的matplotlib库画水平直线时可能遇到的问题,并提供了解决方法。通过导入numpy和matplotlib.pyplot模块,设置绘图对象的宽度和高度,以及使用plot函数绘制水平直线,可以解决该问题。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • 第四章高阶函数(参数传递、高阶函数、lambda表达式)(python进阶)的讲解和应用
    本文主要讲解了第四章高阶函数(参数传递、高阶函数、lambda表达式)的相关知识,包括函数参数传递机制和赋值机制、引用传递的概念和应用、默认参数的定义和使用等内容。同时介绍了高阶函数和lambda表达式的概念,并给出了一些实例代码进行演示。对于想要进一步提升python编程能力的读者来说,本文将是一个不错的学习资料。 ... [详细]
  • 基于dlib的人脸68特征点提取(眨眼张嘴检测)python版本
    文章目录引言开发环境和库流程设计张嘴和闭眼的检测引言(1)利用Dlib官方训练好的模型“shape_predictor_68_face_landmarks.dat”进行68个点标定 ... [详细]
  • EzPP 0.2发布,新增YAML布局渲染功能
    EzPP发布了0.2.1版本,新增了YAML布局渲染功能,可以将YAML文件渲染为图片,并且可以复用YAML作为模版,通过传递不同参数生成不同的图片。这个功能可以用于绘制Logo、封面或其他图片,让用户不需要安装或卸载Photoshop。文章还提供了一个入门例子,介绍了使用ezpp的基本渲染方法,以及如何使用canvas、text类元素、自定义字体等。 ... [详细]
  • 本文介绍了使用Spark实现低配版高斯朴素贝叶斯模型的原因和原理。随着数据量的增大,单机上运行高斯朴素贝叶斯模型会变得很慢,因此考虑使用Spark来加速运行。然而,Spark的MLlib并没有实现高斯朴素贝叶斯模型,因此需要自己动手实现。文章还介绍了朴素贝叶斯的原理和公式,并对具有多个特征和类别的模型进行了讨论。最后,作者总结了实现低配版高斯朴素贝叶斯模型的步骤。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
author-avatar
Yc--Amy_435
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有